
#include "image.h"

#define BMP_WIDTH  256
#define BMP_HEIGHT 256

// ----- Macros ---------------------------------------------------------------

#define COORD                           8320
#define RECTANGLE                        100
#define RADIOACTIVE_SIGN                 200

#define WIN1_HIDE                        0x01
#define WIN2_HIDE                        0x02
#define WIN3_HIDE                        0x03

#define WIN1_SHOW                        0x04
#define WIN2_SHOW                        0x05
#define WIN3_SHOW                        0x06
 
#define MV_CAM_SLOW_DOWN                 0x07
#define MV_CAM_START_MOVING_FAST         0x08
#define MV_CAM_ZOOM_IN                   0x09
#define MV_CAM_ZOOM_OUT                  0x0a

#define ONLINE_PROGRAMMING_START         0x0b00
#define ONLINE_PROGRAMMING_STOP          0x0c00
#define ONLINE_PROGRAMMING_RESET         0x0d00
#define ONLINE_PROGRAMMING_INFO          0x0d01
#define ONLINE_PROGRAMMING_RUN_FORWARD   0x0e00
#define ONLINE_PROGRAMMING_RUN_BACKWARD  0x0f00

#define ALPHA_SPEED_1_5                  0x1f
#define ALPHA_SPEED_2_5                  0x2f
#define ALPHA_SPEED_3_5                  0x3f
#define ALPHA_SPEED_4_5                  0x4f
#define ALPHA_SPEED_5_5                  0x5f

#define BETA_SPEED_1_5                   0x6f
#define BETA_SPEED_2_5                   0x7f
#define BETA_SPEED_3_5                   0x8f
#define BETA_SPEED_4_5                   0x9f
#define BETA_SPEED_5_5                   0xaf

#define GAMMA_SPEED_1_5                  0xbf
#define GAMMA_SPEED_2_5                  0xcf
#define GAMMA_SPEED_3_5                  0xdf
#define GAMMA_SPEED_4_5                  0xef
#define GAMMA_SPEED_5_5                  0xff

#define NONMOBILE_CAMERA_MENU            0xfe

#define ABOUT             0xfffe
#define CLOSE_APPLICATION 0xffff

#define NofElements       0x09
// ----------------------------------------------------------------------------


// ----- MS Windows specific variables ----------------------------------------

MSG            Message , MessageTh , MessageCam , MessageMvCam , MessageAbout;
HWND           WindowID , Monitor , Camera , MvCamera , About;
WNDCLASSEX     WndClass;

// ----------------------------------------------------------------------------

// ----- Mobile & NonMobile camera's data -------------------------------------------------

double NonMobileCamPos[3] = { 80.0 , 0.0 , 30.0 };
double NonMobileCamDir[3] = { 0.0 , 0.0 , 20.0 };

short  MvCamVisible  = 1;
short  Current_Index = 0;
double MvCamZoom     = 0.0;

double Mobile_Camera_Coord[COORD][3];

// ----------------------------------------------------------------------------

double Alpha_Incr = 1.54321;
double Beta_Incr  = 1.54321;
double Gamma_Incr = 1.54321;

short OnlineProgramming          =   0;
int StepsRegistered              =   0;
short ProgramIsRunning           =   0;
unsigned int CurrentProgramIndex =   0;  
short BlinkingDiode              =   1;

float RadioactiveSignRot         = 0.0;

// ----------------------------------------------------------------------------

// ----- Key flags and several global MS Windows specific menu handles ---
short ShiftPressed = 0;

short UpArrowPressed    = 0;
short DownArrowPressed  = 0;
short LeftArrowPressed  = 0;
short RightArrowPressed = 0;
short PageUpPressed     = 0;
short PageDownPressed   = 0;

short ShutDown = 0;

HMENU hMenu , MainPullDown , Win1_Menu , Win2_Menu , Win3_Menu ,
      Application, Programming, Online, Run, Settings , Rotation , 
      Alpha , Beta , Gamma , ASpeed , BSpeed , GSpeed;           // main window menu handles

// ----------------------------------------------------------------------------

HMENU MobileCameraMenu;

// ----------------------------------------------------------------------------

HMENU NonMobileCameraMenu;

// ----- Type intended for thread identification ------------------------------
enum Who_is_it { Main_Window , Camera_Thread , Mobile_Camera , About_Thread };
// ----------------------------------------------------------------------------

// ----- Structure contains some parametres used to control robot motion ------
struct RobotData
{
      double alpha;
      double beta;
      double gamma;
      
      double c1_length;
      double c2_length;
   
      short alpha_change;
      short beta_change;

      RobotData() : alpha( -20.0 ) , beta( 30.0 ) , gamma( -30.0 ) , alpha_change( 0 ) , beta_change( 0 ){}
      
      RobotData operator = ( const RobotData factor )
      {
            alpha        = factor.alpha;
            beta         = factor.beta;
            gamma        = factor.gamma;
            c1_length    = factor.c1_length;
            c2_length    = factor.c2_length;
            alpha_change = factor.alpha_change;
            beta_change  = factor.beta_change;
            
            return *(this);
      }
};

RobotData ProgramStorage[1000];

// ----------------------------------------------------------------------------

unsigned long  threadID_1 , threadID_2 , mv_cam_thread , about_thread;

// ----------------------------------------------------------------------------

OpenGLFonts    FontObj;
WinContexts    renCtx , MonCtx , CamCtx , MvCamCtx , AboutCtx;
RobotData      Steering;

VisualElementTri< double , unsigned int > ElementsArray[ NofElements ];

// ----- light ----------------------------------------------------------------

GLfloat light_ambient [] = { 0.7,0.7,0.7,1.0 };
GLfloat light_diffuse [] = { 0.9,0.9,0.9,1.0 };
GLfloat light_specular[] = { 0.6,0.6,0.6,1.0 };
GLfloat light_position[] = { 20.0, -20.0, 20.0, 0.0 };

// ----------------------------------------------------------------------------

void CompileRectangle( void )
{
      glNewList(RECTANGLE,GL_COMPILE);
      
          glBegin(GL_QUADS);
            
             glVertex3f( -20.0 , 20.0 , -0.7 );
             glVertex3f(  20.0 , 20.0 , -0.7 );
             glVertex3f(  20.0 ,-20.0 , -0.7 );
             glVertex3f( -20.0 ,-20.0 , -0.7 );
             
          glEnd();
          
      glEndList();
}

// ------------------------------------------------------------------------------

void CompileRadioactiveSign( void )
{
      glNewList( RADIOACTIVE_SIGN , GL_COMPILE );
      
          glLineWidth(3.0);
          glColor3f(0.0,1.0,0.0);
          
          glBegin(GL_LINE_LOOP);
            
             glVertex2f( 0.65 , 1.14 );
             glVertex2f(-0.65 , 1.14 );
             glVertex2f(-3.46 , 6.00 );
             glVertex2f(-6.92 , 0.00 );
             glVertex2f(-1.31 , 0.00 );
             glVertex2f(-0.65 ,-1.14 );
             glVertex2f(-3.46 ,-6.00 );
             glVertex2f( 3.46 ,-6.00 );
             glVertex2f( 0.65 ,-1.14 );
             glVertex2f( 1.31 , 0.00 );
             glVertex2f( 6.92 ,-0.00 );
             glVertex2f( 3.46 , 6.00 );

          glEnd();
          
       glLineWidth(1.0);   
       
      glEndList();
}


// ------------------------------------------------------------------------------

void InitOpenGL( Who_is_it Thread )
{
      short i;

      glClearColor( 0.0, 0.0, 0.0, 0.0 );

        glLightfv( GL_LIGHT0 , GL_AMBIENT  , light_ambient );
        glLightfv( GL_LIGHT0 , GL_DIFFUSE  , light_diffuse );
        glLightfv( GL_LIGHT0 , GL_SPECULAR , light_specular);
        glLightfv( GL_LIGHT0 , GL_POSITION , light_position);

        if( Thread == Main_Window )
        {
            string FileNames[ NofElements ];

             FileNames[0].assign( ".\\data\\platform-prj.tri"     );
             FileNames[1].assign( ".\\data\\main_beam-prj.tri"    );
             FileNames[2].assign( ".\\data\\beam_1-prj.tri"       );
             FileNames[3].assign( ".\\data\\ext_engine_1-prj.tri" );
             FileNames[4].assign( ".\\data\\int_engine_1-prj.tri" );
             FileNames[5].assign( ".\\data\\beam_2-prj.tri"       );
             FileNames[6].assign( ".\\data\\ext_engine_2-prj.tri" );
             FileNames[7].assign( ".\\data\\int_engine_2-prj.tri" );
             FileNames[8].assign( ".\\data\\camera.tri"           );
        
             const float colors[9][4] = { {0.8,0.8,0.0,1.0} , {0.6,0.0,0.0,1.0} , {0.0,0.0,0.6,1.0} , {0.0,0.7,0.0,1.0},
                                          {0.8,0.8,0.8,1.0}, {0.0,0.0,0.6,1.0} , {0.0,0.7,0.0,1.0} , {0.8,0.8,0.8,1.0},
                                          {0.0,1.0,0.0,1.0} };
        
             for( i = 0; i < NofElements; i++ )
             {
                  ElementsArray[ i ].vtkInitElement( FileNames[ i ] , colors[ i ] );
            
                  if( !ElementsArray[ i ].vtkGood() )
                  {
                         MessageBox( NULL , FileNames[ i ].c_str() , "Cannot load the following file:" , MB_OK|MB_ICONERROR );
                  }
             }
        }
        else
        {
            for( i = 0; i < NofElements; i++ ) ElementsArray[ i ].vtkCompileDisplayList();
        }
        
      //CompileRectangle();  // rectangle that may be treated as a base underteath the platform
        
      glEnable( GL_LIGHTING );
      glEnable( GL_LIGHT0 );
      glEnable( GL_DEPTH_TEST );
}

// ------------------------------------------------------------------------------

void InitMonitor()
{
      glClearColor( 0.0, 0.0, 0.0, 0.0 );
      glPointSize(8.0);
      CompileRadioactiveSign();
      FontObj.InitFonts();
}

// ------------------------------------------------------------------------------

void InitAbout()
{
    glClearColor( 0.0, 0.0, 0.0, 0.0 );
        
    FontObj.InitFonts();
    
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
    glTexImage2D(GL_TEXTURE_2D, 0, 3, BMP_WIDTH , BMP_HEIGHT , 0, GL_RGB, GL_UNSIGNED_BYTE, &data[0][0][0]);
    glTexParameterf( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S     , GL_CLAMP);
    glTexParameterf( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T     , GL_CLAMP);
    glTexParameterf( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST);
    glTexParameterf( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glEnable(GL_TEXTURE_2D);
    glShadeModel(GL_FLAT);
}

// ------------------------------------------------------------------------------

void DrawAbout( void )
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
 
       glTexCoord2f(0.0, 0.0); glVertex3f(-28.0, -28.0, 0.0);
       glTexCoord2f(0.0, 1.0); glVertex3f(-28.0, 28.0, 0.0);
       glTexCoord2f(1.0, 1.0); glVertex3f(28.0, 28.0, 0.0);
       glTexCoord2f(1.0, 0.0); glVertex3f(28.0, -28.0, 0.0);

    glEnd();
    glDisable(GL_TEXTURE_2D);
    glColor3f(0.7,0.7,0.7);

    glBegin(GL_LINES);
      glVertex2s(-50,-35);
      glVertex2s( 50,-35);
    glEnd();
    
    FontObj.DisplayString( "author: rafal pielech" , -45 , -45 );
    FontObj.DisplayString( "rafalpielech@hotmail.com" , -47 , -55 );
    
    glColor3f(0.5,0.5,0.5);
    FontObj.DisplayString( "Win32 & OpenGL API" , -37 , -66 );

}

// ------------------------------------------------------------------------------

void ChangeAbout( int width , int height )
{
	glViewport( 0 , 0 , width , height );

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 180.0);
    
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      gluLookAt(0,-20,130,0,-20,0,0,1,0);
      

      DrawAbout();
      AboutCtx.SwpBuffers();
}

// ------------------------------------------------------------------------------

void MachineBrain( RobotData * data )
{
      Matrix<double> common , T1_0 , T2_1 , T2_0;
            
      ElementsArray[0].vtkTransformEl( common.GetArray() );
      
      common.Translate( 0.0 , 0.0 , 7.0 );
      ElementsArray[1].vtkTransformEl( common.GetArray() );
      
      common.LoadIdentity();
      common.Translate( 0.0 , 0.0 , 18.5 );
      common.Rotate( data->alpha , 'y' );
      
      T1_0 = common;
      
      ElementsArray[2].vtkTransformEl( common.GetArray() );
      common.LoadIdentity();
      
      Simple_Vector<double> cylinder_top( 15.75 , 0.0 , -2.75 ) , cylinder_bottom( 11.64 , 0.0 , 7.45 ) , cylinder;
      
      cylinder_top = T1_0.MultVector( cylinder_top );
      
      cylinder = (cylinder_top - cylinder_bottom);
      
      common.Translate( cylinder_bottom );
      common.Rotate( (90.0 - cylinder.AngleVectAxis('x')) , 'y' );
      ElementsArray[3].vtkTransformEl( common.GetArray() );
      
      common.LoadIdentity();
      common.Translate( cylinder_top );
      common.Rotate( (90.0 - cylinder.AngleVectAxis('x')) , 'y' );
      ElementsArray[4].vtkTransformEl( common.GetArray() );
      
      data->c1_length = cylinder.Length();
      
      if( data->c1_length < 21.0 && data->c1_length > 13.0 )
      {
            data->alpha_change = 0;
      }
      else
      {
            if( data->c1_length > 21.0 ) data->alpha_change = 1; else data->alpha_change = -1;
      }

      Simple_Vector<double> beam_2( 38.0 , 0.0 , 0.0 );
      
      beam_2 = T1_0.MultVector( beam_2 );

      common.LoadIdentity();
      common.Translate( beam_2 );
      common.Rotate( (data->alpha + data->beta) , 'y' );
      ElementsArray[5].vtkTransformEl( common.GetArray() );
     
      common.LoadIdentity();
      
      T2_1.Translate( 38.0 , 0.0 , 0.0 );
      T2_1.Rotate( data->beta , 'y' );

      T2_0 = (T1_0 * T2_1);
      
      cylinder_top.SetElement( 0 , 9.13 );
      cylinder_top.SetElement( 2 ,-2.06 );
      cylinder_top = T2_0.MultVector( cylinder_top );
      
      cylinder_bottom.SetElement( 0 , 27.75 );
      cylinder_bottom.SetElement( 2 ,-2.75 );
      cylinder_bottom = T1_0.MultVector( cylinder_bottom );
      
      cylinder = (cylinder_top - cylinder_bottom);
      
      common.LoadIdentity();
      common.Translate( cylinder_bottom );
      
      double oper = (cylinder.GetElement(2)/fabs(cylinder.GetElement(2)));

      common.Rotate( (90 - oper * cylinder.AngleVectAxis('x')) , 'y' );
      
      ElementsArray[6].vtkTransformEl( common.GetArray() );
      
      common.LoadIdentity();
      common.Translate( cylinder_top );
      common.Rotate( (90 - oper * cylinder.AngleVectAxis('x')) , 'y' );
      ElementsArray[7].vtkTransformEl( common.GetArray() );
      
      data->c2_length = cylinder.Length();
      
      if( data->c2_length < 16.0 && data->c2_length > 10.0 )
      {
            data->beta_change = 0;
      }
      else
      {
            if( data->c2_length > 16.0 ) data->beta_change = 1; else data->beta_change = -1;
      }

}

// ------------------------------------------------------------------------------

void Draw( Who_is_it Thread )
{
    if( Thread == About_Thread ) return;
      
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
    if( Thread == Main_Window )
    {
          if( ProgramIsRunning ) MachineBrain( &ProgramStorage[CurrentProgramIndex] ); else MachineBrain( &Steering );
    }

       
    if( Thread == Camera_Thread )
    {
          glMatrixMode(GL_MODELVIEW);
          glLoadIdentity();
	    gluLookAt( NonMobileCamPos[0] , NonMobileCamPos[1] , NonMobileCamPos[2] , 
	               NonMobileCamDir[0] , NonMobileCamDir[1] , NonMobileCamDir[2] , 0.0 , 0.0 , 1.0 );
	    
	    glPushMatrix();
	    
	      glTranslated( Mobile_Camera_Coord[Current_Index][0] , 
	                    Mobile_Camera_Coord[Current_Index][1] ,
	                    Mobile_Camera_Coord[Current_Index][2] );
	              
	      ElementsArray[ 8 ].vtkShowElement();
	    
	    glPopMatrix();
     }

    glPushMatrix();
    
        if( ProgramIsRunning ) 
        {
            glRotated( ProgramStorage[CurrentProgramIndex].gamma , 0 , 0 , 1 );
        }
        else
        {
            glRotated( Steering.gamma , 0 , 0 , 1 );
        }
        
        
        for( short i = 0; i < 8; i++ ) ElementsArray[ i ].vtkShowElement();
           
    glPopMatrix();
}

// ------------------------------------------------------------------------------

void DrawParametres( void )
{
      glClear( GL_COLOR_BUFFER_BIT );
      glColor3f(0.8,0.8,0.8);
      
      FontObj.DisplayString( "Alpha: " , 20 , -25 );
      
      if( ProgramIsRunning )
      {
            FontObj.DisplayRealNumber( ProgramStorage[CurrentProgramIndex].alpha , 4 , 84 , -25 );
      }
      else
      {
            FontObj.DisplayRealNumber( Steering.alpha , 4 , 84 , -25 );
      }
      
      FontObj.DisplayString( " [deg]" , 168 , -25 );
      
      FontObj.DisplayString( "Beta: " , 20 , -45 );

      if( ProgramIsRunning )
      {
            FontObj.DisplayRealNumber( ProgramStorage[CurrentProgramIndex].beta , 4 , 84 , -45 );
      }
      else
      {
            FontObj.DisplayRealNumber( Steering.beta , 4 , 84 , -45 );
      }

      FontObj.DisplayString( " [deg]" , 168 , -45 );
      
      FontObj.DisplayString( "Gamma: " , 20 , -65 );

      if( ProgramIsRunning )
      {
            FontObj.DisplayRealNumber( ProgramStorage[CurrentProgramIndex].gamma , 4 , 84 , -65 );
      }
      else
      {
            FontObj.DisplayRealNumber( Steering.gamma , 4 , 84 , -65 );
      }

      FontObj.DisplayString( " [deg]" , 168 , -65 );
      
      FontObj.DisplayString( "Cylinder 1 length: " , 20 , -95 );

      if( ProgramIsRunning )
      {
            FontObj.DisplayRealNumber( ProgramStorage[CurrentProgramIndex].c1_length , 4 , 210 , -95 );
      }
      else
      {
            FontObj.DisplayRealNumber( Steering.c1_length , 4 , 210 , -95 );
      }
            
      FontObj.DisplayString( "Cylinder 2 length: " , 20 , -115 );

      if( ProgramIsRunning )
      {
            FontObj.DisplayRealNumber( ProgramStorage[CurrentProgramIndex].c2_length , 4 , 210 , -115 );
      }
      else
      {
            FontObj.DisplayRealNumber( Steering.c2_length , 4 , 210 , -115 );
      }
      
      FontObj.DisplayString( "Mobile camera position: " , 20 , -145 );
      
      FontObj.DisplayString( "[ " , 30 , -165 );
      
      FontObj.DisplayRealNumber( Mobile_Camera_Coord[Current_Index][0] , 2 , 46 , -165 );
      FontObj.DisplayString( "," , 110 , -165 );
      
      FontObj.DisplayRealNumber( Mobile_Camera_Coord[Current_Index][1] , 2 , 126 , -165 );
      FontObj.DisplayString( "," , 180 , -165 );
      
      FontObj.DisplayRealNumber( Mobile_Camera_Coord[Current_Index][2] , 2 , 196 , -165 );
      FontObj.DisplayString( "]" , 258 , -165 );
      
        glBegin(GL_LINES);
               
              glVertex2s( 15  , -180 );
              glVertex2s( 263 , -180 );
               
        glEnd();
            
           FontObj.DisplayString( " Online programming " , 40 , -200 );

        glBegin(GL_LINES);
               
              glVertex2s( 15  , -210 );
              glVertex2s( 263 , -210 );
               
        glEnd();

        if( OnlineProgramming )
        {
            
            if( BlinkingDiode <= 5 )  glColor3f(0.0,1.0,0.0); else glColor3f(0.0,0.0,0.0);
      
            glBegin(GL_POINTS);
        
               glVertex2s(24,-225);
         
            glEnd();
            
            glColor3f(0.7,0.7,0.7);            
            FontObj.DisplayString( "Registering configurations:" , 40 , -235 );
            
            FontObj.DisplayInteger( StepsRegistered , 60 , -260 );
            FontObj.DisplayString( "conf. registered." , 100 , -260 );

            if( BlinkingDiode > 9 ) 
            {
                  BlinkingDiode = 1; 
                  return;
            }
                        
            BlinkingDiode++; 
        }
        else
        {
            glColor3f(1.0,0.0,0.0);
      
            glBegin(GL_POINTS);
        
               glVertex2s(24,-225);
         
            glEnd();
            
            glColor3f(0.7,0.7,0.7);
            FontObj.DisplayString( "Registering conf. suspended." , 40 , -235 );
            
            FontObj.DisplayInteger( StepsRegistered , 60 , -260 );
            FontObj.DisplayString( "conf. registered." , 100 , -260 );
        }
        
        if( ProgramIsRunning )
        {
            glColor3f(1.0,1.0,1.0);
            
            glBegin(GL_LINES);
               
                glVertex2s( 15  , -275 );
                glVertex2s( 293 , -275 );
               
            glEnd();

            glColor3f(0.7,0.7,0.7);
            FontObj.DisplayString( "Program is running !" , 50 , -300 );
            
            glPushMatrix();
               
               glTranslatef(25.0,-295.0,0.0);
               glRotatef( -RadioactiveSignRot , 0 , 0 , 1 );
               glCallList(RADIOACTIVE_SIGN);
               
               glLoadIdentity();
               
               glTranslatef(270.0,-295.0,0.0);
               glRotatef( -RadioactiveSignRot , 0 , 0 , 1 );
               glCallList(RADIOACTIVE_SIGN);

               RadioactiveSignRot += 20.0;
               if( RadioactiveSignRot > 360.0 ) RadioactiveSignRot = 0.0;
            
            glPopMatrix();
        }
}

// ------------------------------------------------------------------------------

void SetMovingCameraCoordinates( void )
{
      short i;
      double angle = 0.0;
      
      for( i = 0; i < COORD; i++ )
      {
            Mobile_Camera_Coord[i][0] = 60 * cos( angle );
            Mobile_Camera_Coord[i][1] = 25.0;
            Mobile_Camera_Coord[i][2] = 60 * sin( angle );
            angle += 0.03125;
      }
}

// ------------------------------------------------------------------------------

void ChangeSceneSize( int width , int height , Who_is_it Thread )
{
    	if ( height == 0 ) height = 1;

	if( Thread == Camera_Thread )
	{
          glViewport( 0 , 0 , width , height );
	    glMatrixMode(GL_PROJECTION);
	    glLoadIdentity();
          gluPerspective( 45.0 , 1 , 0.1 , 200.0 );
          return;
	}
	else
	{
	      glViewport( 0 , 0 , width , height );
	      glMatrixMode(GL_PROJECTION);
	      glLoadIdentity();
            gluPerspective( 45.0 , (GLfloat)width/(GLfloat)height , 0.1 , 200.0 );
	}
		
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	if( Thread == Main_Window )
	{
	      gluLookAt( -50.0 , -50.0 , 50.0 , 0.0 , 0.0 , 20.0 , 0.0 , 0.0 , 1.0 );
	}
	if( Thread == Mobile_Camera )
	{
	      SetMovingCameraCoordinates();
            gluLookAt( Mobile_Camera_Coord[0][0] , Mobile_Camera_Coord[0][1] , Mobile_Camera_Coord[0][2] ,
                       0.0 , 0.0 , 20.0 , 0.0 , 0.0 , 1.0 );
	}
}

// ------------------------------------------------------------------------------

void ChangeMonitorSize( int width , int height )
{
	glViewport( 0 , 0 , width , height );

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluOrtho2D( 0 , width , -height , 0 );

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

// ------------------------------------------------------------------------------

void RunTheProgram( short mode )
{
      ProgramIsRunning = 1;
      
      switch( mode )
      {
            case ONLINE_PROGRAMMING_RUN_FORWARD:

                                            for( CurrentProgramIndex=0; CurrentProgramIndex<=StepsRegistered; CurrentProgramIndex++ )
                                            {
                                                       Draw( Main_Window );
                                                       renCtx.SwpBuffers();
                                             }
                                             break;
             
             case ONLINE_PROGRAMMING_RUN_BACKWARD:

                                             for( CurrentProgramIndex=StepsRegistered; CurrentProgramIndex>0; CurrentProgramIndex-- )
                                             {
                                                       Draw( Main_Window );
                                                       renCtx.SwpBuffers();
                                              }
                                              break;
      }
      
      
      ProgramIsRunning = 0;
}

// ------------------------------------------------------------------------------

LRESULT CALLBACK WindowProcedure( HWND WinHand , UINT Message , WPARAM wParam , LPARAM lParam )
{

   switch( Message )
   {
     case WM_CREATE:   
                       break;
     case WM_COMMAND:  
                       if( WinHand == Camera && (LOWORD(wParam) == NONMOBILE_CAMERA_MENU) )
                       {
                           MessageBox(NULL,"While this window is active you can use arrows, PageUp and PageDown\n to change the point of view. When Shift is hold down\n these keys affect LookAt vector." , "Info" , MB_ICONINFORMATION);
                       }
     
                       if( WinHand == MvCamera )
                       {
                           if( LOWORD(wParam) == MV_CAM_SLOW_DOWN  ) MvCamVisible = 0;
                           if( LOWORD(wParam) == MV_CAM_START_MOVING_FAST ) MvCamVisible = 1;
                           if( LOWORD(wParam) == MV_CAM_ZOOM_IN  ) MvCamZoom -= 1.5;
                           if( LOWORD(wParam) == MV_CAM_ZOOM_OUT ) MvCamZoom += 1.5;
                       }

                       if( WinHand == WindowID )
                       {
                          switch(LOWORD(wParam))
                          {
                              case WIN3_SHOW: ShowWindow( MvCamera , SW_SHOW ); break;
                              case WIN2_SHOW: ShowWindow( Camera , SW_SHOW ); break;
                              case WIN1_SHOW: ShowWindow( Monitor , SW_SHOW ); break;
                              case WIN3_HIDE: ShowWindow( MvCamera , SW_HIDE ); break;
                              case WIN2_HIDE: ShowWindow( Camera , SW_HIDE ); break;
                              case WIN1_HIDE: ShowWindow( Monitor , SW_HIDE ); break;
                              
                              case ONLINE_PROGRAMMING_START: 
                                                             OnlineProgramming = 1;
                                                             ModifyMenu( Online , 1 , MF_BYPOSITION|MF_STRING , 
                                                                         ONLINE_PROGRAMMING_STOP ,  "S&top" );
                                                             ModifyMenu( Online , 0 , MF_BYPOSITION|MF_STRING|MF_GRAYED , 
                                                                         ONLINE_PROGRAMMING_START ,  "&Start" );
                                                             break;
                              case ONLINE_PROGRAMMING_STOP : 
                                                             OnlineProgramming = 0;
                                                             ModifyMenu( Online , 2 , MF_BYPOSITION|MF_STRING , 
                                                                         ONLINE_PROGRAMMING_RESET ,  "&Reset" );
                                                             ModifyMenu( Online , 1 , MF_BYPOSITION|MF_STRING|MF_GRAYED , 
                                                                         ONLINE_PROGRAMMING_STOP ,  "S&top" );

                                                             ModifyMenu( Run , 0 , MF_BYPOSITION|MF_STRING , 
                                                                         ONLINE_PROGRAMMING_RUN_FORWARD , "Run &forward" );
                                                             ModifyMenu( Run , 1 , MF_BYPOSITION|MF_STRING , 
                                                                         ONLINE_PROGRAMMING_RUN_BACKWARD , "Run &backward" );
                                                             break;
                                                             
                              case ONLINE_PROGRAMMING_RESET: 
                                                             StepsRegistered = 0; 
                                                             ModifyMenu( Online , 2 , MF_BYPOSITION|MF_STRING|MF_GRAYED , 
                                                                         ONLINE_PROGRAMMING_RESET ,  "&Reset" );
 
                                                             ModifyMenu( Online , 0 , MF_BYPOSITION|MF_STRING , 
                                                                         ONLINE_PROGRAMMING_START ,  "&Start" );

                                                             ModifyMenu( Run , 0 , MF_BYPOSITION|MF_STRING|MF_GRAYED , 
                                                                         ONLINE_PROGRAMMING_RUN_FORWARD , "Run &forward" );
                                                             ModifyMenu( Run , 1 , MF_BYPOSITION|MF_STRING|MF_GRAYED , 
                                                                         ONLINE_PROGRAMMING_RUN_BACKWARD , "Run &backward" );
                                                             
                                                             break;
                              
                              case ONLINE_PROGRAMMING_INFO:
                                                             MessageBox(NULL,"In order to program the robot do the following:\n\n1.Click on Programming->Online->Start\n2.Use arrows, PageUp and PageDown keys while \"Kinematics\" window is active.\n3.Program will rewrite up to 1000 robot's configurations.\n4.If program is ready then click on Programming->Online->Stop.\n5.When each of the preceding steps went ok you can run the program by clicking Run->...\n","Info",MB_ICONINFORMATION);
                                                             break;                
                              
                              case ALPHA_SPEED_1_5: Alpha_Incr = 1.54321; break;
                              case ALPHA_SPEED_2_5: Alpha_Incr = 2.54321; break;
                              case ALPHA_SPEED_3_5: Alpha_Incr = 3.54321; break;
                              case ALPHA_SPEED_4_5: Alpha_Incr = 4.54321; break;
                              case ALPHA_SPEED_5_5: Alpha_Incr = 5.54321; break;
                              
                              case BETA_SPEED_1_5: Beta_Incr = 1.54321; break;
                              case BETA_SPEED_2_5: Beta_Incr = 2.54321; break;
                              case BETA_SPEED_3_5: Beta_Incr = 3.54321; break;
                              case BETA_SPEED_4_5: Beta_Incr = 4.54321; break;
                              case BETA_SPEED_5_5: Beta_Incr = 5.54321; break;
                              
                              case GAMMA_SPEED_1_5: Gamma_Incr = 1.54321; break;
                              case GAMMA_SPEED_2_5: Gamma_Incr = 2.54321; break;
                              case GAMMA_SPEED_3_5: Gamma_Incr = 3.54321; break;
                              case GAMMA_SPEED_4_5: Gamma_Incr = 4.54321; break;
                              case GAMMA_SPEED_5_5: Gamma_Incr = 5.54321; break;
                              
                              case ONLINE_PROGRAMMING_RUN_FORWARD : RunTheProgram(ONLINE_PROGRAMMING_RUN_FORWARD); break;
                              case ONLINE_PROGRAMMING_RUN_BACKWARD: RunTheProgram(ONLINE_PROGRAMMING_RUN_BACKWARD); break;
                              
                              case CLOSE_APPLICATION: SendMessage( WindowID , WM_CLOSE , 0 , 0 ); break;
                              case ABOUT:
                                          ShowWindow( About , SW_SHOW );
                                          Draw( About_Thread );
                                          AboutCtx.SwpBuffers();
                                          break;
                          }
                       }
                       break;
     case WM_NOTIFY:
                       break;
                       
     case WM_SHOWWINDOW:
                         if( WinHand == MvCamera )
                         {
                              if( wParam ) MvCamVisible = 1; else MvCamVisible = 0;
                         }

                         if( WinHand == About && wParam )
                         {
                              DrawAbout();
                              AboutCtx.SwpBuffers();
                         }
                         break;
                       
     case WM_KEYDOWN:
                       
                       if( WinHand == WindowID && !ProgramIsRunning )
                       {
                             switch( wParam )
                             {
                                 case VK_UP   : UpArrowPressed    = 1; break;
                                 case VK_DOWN : DownArrowPressed  = 1; break;
                                 case VK_LEFT : LeftArrowPressed  = 1; break;
                                 case VK_RIGHT: RightArrowPressed = 1; break;
                                 case VK_PRIOR: PageUpPressed     = 1; break;
                                 case VK_NEXT : PageDownPressed   = 1; break;
                             }
                       }

                       if( WinHand == Camera )
                       {
                            switch( wParam )
                            {
                                case VK_SHIFT:  ShiftPressed = 1; break;
                                case VK_UP:
                                             if( ShiftPressed ) NonMobileCamDir[1] += 2.0; else NonMobileCamPos[1] += 2.0;
                                break;
                                
                                case VK_DOWN:
                                              if( ShiftPressed ) NonMobileCamDir[1] -= 2.0; else NonMobileCamPos[1] -= 2.0;
                                break;
                                
                                case VK_LEFT:
                                               if( ShiftPressed ) NonMobileCamDir[0] -= 2.0; else NonMobileCamPos[0] -= 2.0;
                                break;
                            
                                case VK_RIGHT:
                                               if( ShiftPressed ) NonMobileCamDir[0] += 2.0; else NonMobileCamPos[0] += 2.0;
                                break;               

                                case VK_PRIOR:
                                               if( ShiftPressed ) NonMobileCamDir[2] -= 2.0; else NonMobileCamPos[2] -= 2.0;
                                break;
                                
                                case VK_NEXT:
                                               if( ShiftPressed ) NonMobileCamDir[2] += 2.0; else NonMobileCamPos[2] += 2.0;
                                break;               
                            }
                            
                       }
                       break;
     
     case WM_KEYUP:
                       if( WinHand == Camera && wParam == VK_SHIFT ) ShiftPressed = 0;

                       if( WinHand == WindowID && !ProgramIsRunning )
                       {
                             switch( wParam )
                             {
                                 case VK_UP   : UpArrowPressed    = 0; break;
                                 case VK_DOWN : DownArrowPressed  = 0; break;
                                 case VK_LEFT : LeftArrowPressed  = 0; break;
                                 case VK_RIGHT: RightArrowPressed = 0; break;
                                 case VK_PRIOR: PageUpPressed     = 0; break;
                                 case VK_NEXT : PageDownPressed   = 0; break;
                             }
                       }
                       break;
     case WM_SIZE:     
                       if( WinHand == Monitor )
                       {
                            ChangeMonitorSize( (int)(LOWORD(lParam)) , (int)(HIWORD(lParam)));
                            DrawParametres();
                            MonCtx.SwpBuffers();
                       }
                       
                       if( WinHand == WindowID )
                       {
                             ChangeSceneSize( (int)(LOWORD(lParam)) , (int)(HIWORD(lParam)) , Main_Window );
                             Draw( Main_Window );
                             renCtx.SwpBuffers();
                       }
                       
                       if( WinHand == Camera )
                       {
                             ChangeSceneSize( (int)(LOWORD(lParam)) , (int)(HIWORD(lParam)) , Camera_Thread );
                             Draw( Camera_Thread );
                             CamCtx.SwpBuffers();
                       }

                       if( WinHand == MvCamera )
                       {
                             ChangeSceneSize( (int)(LOWORD(lParam)) , (int)(HIWORD(lParam)) , Mobile_Camera );
                       }
                       
                       if( WinHand == About )
                       {
                             ChangeAbout( (int)(LOWORD(lParam)) , (int)(HIWORD(lParam)));
                       }
                       
                       break;
     case WM_MOVE:    
                       if( IsWindowVisible(WindowID) )
                       {
                           Draw( Main_Window );
                           renCtx.SwpBuffers();
                       }
                       
                       if( IsWindowVisible(About) )
                       {
                          DrawAbout();
                          AboutCtx.SwpBuffers();
                       }
     
                       break;

     case WM_CLOSE:     
                       if( WinHand == About )
                       {
                            ShowWindow(About,SW_HIDE);
                       }
                        
                       if( WinHand == Camera )
                       {
                            CamCtx.deleteCtx();
                            DestroyWindow( Camera );
                       }
                       if( WinHand == Monitor )
                       {
                            MonCtx.deleteCtx();
                            DestroyWindow( Monitor );
                       }
                       if( WinHand == MvCamera )
                       {
                            MvCamCtx.deleteCtx();
                            DestroyWindow( MvCamera );
                       }
                       if( WinHand == WindowID )
                       {
                            AboutCtx.deleteCtx();
                            DestroyWindow( About );

                            SendMessage( Monitor  , WM_CLOSE , 0 , 0 );
                            SendMessage( Camera   , WM_CLOSE , 0 , 0 );
                            SendMessage( MvCamera , WM_CLOSE , 0 , 0 );
                            
                            renCtx.deleteCtx();
                            DestroyWindow( WindowID );
                       }
                       break;
     case WM_DESTROY:
                       PostQuitMessage( 0 );
                       break;
     default:
                       return DefWindowProc( WinHand , Message , wParam , lParam );
   }
  return 0;
}

// ------------------------------------------------------------------------------

void CALLBACK TimerProcedure( HWND hwnd , UINT uMsg , UINT idEvent , DWORD dwTime )
{
      if( hwnd == Monitor )      
      {
            DrawParametres();
            MonCtx.SwpBuffers();
      }
      if( hwnd == Camera )
      {
            Draw( Camera_Thread );
            CamCtx.SwpBuffers();
      }
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------

DWORD WINAPI ThreadFunction_1( LPVOID pointer )
{
      
      Monitor = CreateWindowEx( WS_EX_CLIENTEDGE , "OpenGL" , "Mechanism parametres" , 
                                WS_CLIPSIBLINGS | WS_CLIPCHILDREN , 370 , 10 , 360 , 360 ,
                                NULL , NULL , (HINSTANCE)pointer , NULL );
                                  
      if( Monitor == NULL )
      {
            ExitThread(0);
      }
      else
      {
            MonCtx.initRendCtx( Monitor );
            MonCtx.MakeCurrent(1);
            
            if( MonCtx.good() )
            {
                  ShowWindow( Monitor , SW_SHOW );
                  InitMonitor();
                  DrawParametres();
                  MonCtx.SwpBuffers();
            }
            else
            {
                  MonCtx.deleteCtx();
                  DestroyWindow( Monitor );
                  ExitThread(0);
            }
      }
      
      SetTimer( Monitor , 1 , 20 , TimerProcedure );
      
      while( GetMessage( &MessageTh , NULL , 0 , 0 ) > 0 )
      {
             TranslateMessage( &MessageTh );
             DispatchMessage( &MessageTh );
      }
      
      //MessageBox( NULL , "Thread is going to finish it's existence !" , "--- >> Thread info << ---" , MB_OK );
      ExitThread(0);
}

// ------------------------------------------------------------------------------

DWORD WINAPI ThreadFunction_2( LPVOID pointer )
{
      
      Camera = CreateWindowEx( WS_EX_CLIENTEDGE , "OpenGL" , "View from another camera" , 
                                WS_CLIPSIBLINGS | WS_CLIPCHILDREN , 370 , 370 , 360 , 320 ,
                                NULL , NULL , (HINSTANCE)pointer , NULL );
                                  
      if( Camera == NULL )
      {
            ExitThread(0);
      }
      else
      {
            CamCtx.initRendCtx( Camera );
            CamCtx.MakeCurrent(1);
            
            if( CamCtx.good() )
            {
                  NonMobileCameraMenu = CreateMenu();
                  
                  InsertMenu( NonMobileCameraMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_STRING , NONMOBILE_CAMERA_MENU, "&Read me" );
                  SetMenu( Camera , NonMobileCameraMenu ); 

                  ShowWindow( Camera , SW_SHOW );
                  
                  InitOpenGL( Camera_Thread );
                  Draw( Camera_Thread );
                  CamCtx.SwpBuffers();
            }
            else
            {
                  CamCtx.deleteCtx();
                  DestroyWindow( Camera );
                  ExitThread(0);
            }
      }
      
      SetTimer( Camera , 2 , 200 , TimerProcedure );
      
      while( GetMessage( &MessageCam , NULL , 0 , 0 ) > 0 )
      {
             TranslateMessage( &MessageCam );
             DispatchMessage( &MessageCam );
      }
      
      ExitThread(0);
}

// ------------------------------------------------------------------------------

DWORD WINAPI ThreadFunction_3( LPVOID pointer )
{

      short i,j=1;    
      HMENU hMenu;
      
      MvCamera = CreateWindowEx( WS_EX_CLIENTEDGE , "OpenGL" , "View from mobile camera" , 
                                 WS_CLIPSIBLINGS | WS_CLIPCHILDREN , 50 , 370 , 320 , 320 ,
                                 NULL , NULL , (HINSTANCE)pointer , NULL );
                                 
                                  
      if( MvCamera == NULL )
      {
            ExitThread(0);
      }
      else
      {
            MvCamCtx.initRendCtx( MvCamera );
            MvCamCtx.MakeCurrent(1);
            CompileRectangle();
            
            if( MvCamCtx.good() )
            {
                  hMenu = CreateMenu();
                  MobileCameraMenu = CreatePopupMenu();
                  
                  InsertMenu( MobileCameraMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_STRING , MV_CAM_START_MOVING_FAST, "&Move fast" );
                  InsertMenu( MobileCameraMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_STRING , MV_CAM_SLOW_DOWN, "&Slow down" );
                  InsertMenu( MobileCameraMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_SEPARATOR , 0 , "&Separator" );

                  InsertMenu( hMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_STRING|MF_POPUP , (UINT)MobileCameraMenu, "&Modify" );
                  InsertMenu( hMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_STRING , MV_CAM_ZOOM_IN  , "Zoom &In" );
                  InsertMenu( hMenu , 0xFFFFFFFF , MF_BYPOSITION|MF_STRING , MV_CAM_ZOOM_OUT , "Zoom &Out");
                  SetMenu( MvCamera , hMenu ); 

                  ShowWindow( MvCamera , SW_SHOW );
                  
                  InitOpenGL( Mobile_Camera );
                  
                  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
                  
                  glCallList(RECTANGLE);
                  
                  glPushMatrix();
                  
                  glRotated( Steering.gamma , 0 , 0 , 1 );
        
                      for( i = 0; i < 8; i++ ) ElementsArray[ i ].vtkShowElement();
           
                  glPopMatrix();


                  MvCamCtx.SwpBuffers();
            }
            else
            {
                  MvCamCtx.deleteCtx();
                  DestroyWindow( MvCamera );
                  ExitThread(0);
            }
      }
      
      
      while( 1 )
      {
            if( MvCamVisible )
            {
                  if( PeekMessage( &MessageMvCam , NULL , 0 , 0 , PM_REMOVE ) )
                  {
                       if( MessageMvCam.message == WM_QUIT )
                       {
                            break;
                       }

                     TranslateMessage( &MessageMvCam );
                     DispatchMessage( &MessageMvCam );
                  }
            }
            else
            {
                  if( GetMessage( &MessageMvCam , NULL , 0 , 0 ) > 0 )
                  {
                        TranslateMessage( &MessageMvCam );
                        DispatchMessage( &MessageMvCam );
                  }
                  else break;
            }
            
  
                  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
                  
                  glMatrixMode(GL_MODELVIEW);
	            glLoadIdentity();
	            
	            gluLookAt( (Mobile_Camera_Coord[j][0] + MvCamZoom) , (Mobile_Camera_Coord[j][1] + MvCamZoom) , 
	                       (Mobile_Camera_Coord[j][2] + MvCamZoom) ,  0.0 , 0.0 , 20.0 , 0.0 , 0.0 , 1.0 );
                  
                  glCallList(RECTANGLE);
                  
                  if( ++j == COORD ) j = 0;

                  Current_Index = j;
                  
                  glPushMatrix();
                  
                  if( ProgramIsRunning ) 
                  {
                       glRotated( ProgramStorage[CurrentProgramIndex].gamma , 0 , 0 , 1 );
                  }
                  else
                  {
                        glRotated( Steering.gamma , 0 , 0 , 1 );
                  }
                  
        
                      for( i = 0; i < 8; i++ ) ElementsArray[ i ].vtkShowElement();
           
                  glPopMatrix();


                  MvCamCtx.SwpBuffers();
      }
      
      ExitThread(0);
}

// ------------------------------------------------------------------------------

DWORD WINAPI ThreadFunction_4( LPVOID pointer )
{

         About = CreateWindowEx( WS_EX_CLIENTEDGE , "OpenGL" , "About ..." , 
                                 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU, 352 , 370 , 320 , 320 ,
                                 NULL , NULL , (HINSTANCE)pointer , NULL );

         
         if( About == NULL )
         {
               ExitThread(0);
         }
         else
         {
            AboutCtx.initRendCtx( About );
            AboutCtx.MakeCurrent(1);
            
            if( AboutCtx.good() )
            {
                  InitAbout();
                  ShowWindow( About , SW_HIDE );
            }
            else
            {
                  AboutCtx.deleteCtx();
                  DestroyWindow( About );
                  ExitThread(0);
            }
         }
         
         while( GetMessage( &MessageAbout , NULL , 0 , 0 ) > 0 )
         {
                 TranslateMessage( &MessageAbout );
                 DispatchMessage( &MessageAbout );
         }
}

// ------------------------------------------------------------------------------